; def InstalVar macro
; def MFillField macro bereik,level
; def InStalFillField macro
;	  PFillField
; def MPointInRect macro x0,y0,x,y,sizex,sizey,notinrect,inrect
; def MDrawInt macro x,y,integer
; def MUpdateMouse macro
; def MDrawRect macro color,x,y,sizex,sizey
; def InstalDrawRect
;	  DrawRect
; def MUpdateScreenMode13 macro
; def MBlankScreenMode13 macro 
; def MSetVideoMode macro NewMode
; def MRestoreVideoMode macro
; def MDelay macro clockticks
; def MCopyData macro segsource,offsetsource,segdestination,offsetdestination,nr 
; def IntstalCopyData
;	  PCopyData
; def MPrintString macro offsetstring
; def MRandomize macro
; def InstalRandInt macro
; 	  PRandInt
; def InstalPrintInt macro 
;	  PPrintInt
; def MPrintInt macro integer


InstalVar macro
	.FARDATA ;for storing off-line buffer
    videobuffer db 64000 dup (0)
	.DATA
	OldMode db ?
	mousex dw ?
	mousey dw ?
	mousestate dw ?
	oldmousestate dw ?
	powersoften dw 10000,1000,100,10,1
	maxint db "-32768$"
	rand dw ?
	endl db 0Ah,0Dh,"$"
	fieldheight= 13
	fieldwidth= 7
	Field dw fieldheight dup (fieldwidth dup(0))
endm

MFillField macro bereik,level
	push ax
	mov ax,level
	push ax
	mov ax,bereik
	push ax
	call  PFillField
	pop ax
endm

MPointInRect macro x0,y0,x,y,sizex,sizey,notinrect,inrect
; checks if the point (x0,y0) is inside rect(x,y,sizex,sizey)
; if true  then jump to inrect
; if false then jump to notinrect
; changes ax
	mov ax,x
	cmp x0,ax
	jng notinrect
	add ax,sizex
	cmp x0,ax
	jnl notinrect
	
	mov ax,y
	cmp y0,ax
	jng notinrect
	add ax,sizey
	cmp x0,ax
	jnl notinrect
	jmp inrect
endm

MDrawInt macro x,y,integer
; writes a ineger at a specified position
; handy in mode 13h to display integer
	; save context
	push ax
	push dx
	push bx
	; set cursor position
	mov ah,2
		mov dh,x
		mov dl,y
		mov bh,0
	int 10h
	MPrintInt integer
	; restore context
	pop bx
	pop dx
	pop ax
endm

MInitMouse macro
	xor ax, ax			; initialize mouse
	int 33h
	MUpdateMouse
endm	
	
MUpdateMouse macro
	push ax
	push cx
	push dx
	push bx
	mov ax, 3			; get buttons status and position
	int 33h
	mov mousex,cx
	shr mousex,1
	mov mousey,dx
	mov ax,mousestate
	mov oldmousestate,ax
	mov mousestate,bx
	pop bx
	pop dx
	pop cx
	pop ax
endm

MDrawRect macro color,x,y,sizex,sizey
; Macro for easy use of PDrawRect
	push ax
	mov al,color
	cbw
	push ax
	mov ax,x
	push ax
	mov ax,y
	push ax
	mov ax,sizey
	push ax
	mov ax,sizex
	push ax
	call PDrawRect
	pop ax
endm

InstalDrawRect macro
PDrawRect proc near		
; PDrawRect draws a rectangle to videobuffer
; oreder to push: color,x,y,sizex,sizey
	;save dynamic link
    push bp
    ;update bp
    mov bp,sp
	;save context
	push ax
	push cx
	push dx
	push si
	push di
	push bx
	;start with first line
	;rep movsb source in ds:si, destination in es:di
	mov di,seg videobuffer
	mov es,di
	mov di,offset videobuffer
	;adjust di to account for rectx,recty
	mov ax,320
	mul word ptr [bp+8]
	add ax,[bp+10]
	add di,ax
	;add di,rectx+320*recty
	;init cx for loop (height of rectangle)
	mov ax,[bp+12]
	mov cx,[bp+4]
	DRvertical:
	push cx ;save cx on stack so it can be used for rep movsb
	mov cx,[bp+6]
	rep stosb
	;update si and di to point to next line
	;add si,rectwidth
	mov bx,320
	sub bx,[bp+6]	;320-rectwidth
	add di,bx
	pop cx ;restore cx for loop
	loop DRvertical
	;reset ds segment register
	mov ax,@data
	mov ds,ax
	;restore context
	pop bx
	pop di
	pop si
	pop dx
	pop cx
	pop ax
    ;restore bp
    pop bp
    ;return freeing param from stack 
    ret 10
PDrawRect endp
endm

MUpdateScreenMode13 macro
; writes the videobuffer to de video ram 
; great to prevent blinking
	local asciimodeex1_wait1,asciimodeex1_wait2
    ;store context
    push ax
    push cx
    push dx
    push es
    push di
	push ds
	;change registers for rep movsb to video ram
	mov dx,seg videobuffer
	mov ds,dx
	lea si,videobuffer
	mov dx,0A000h
	mov es,dx
	xor di,di
	;wait for vblank to write to video ram
	mov dx, 03dah ; VGA status port             
	asciimodeex1_wait1: ;if in vblank, wait until vblank finishes
	in al, dx
	and al, 8
	jnz asciimodeex1_wait1                 ; busy wait
	asciimodeex1_wait2:                    ; wait until begin of vblank
	in al, dx
	and al, 8
	jz asciimodeex1_wait2                  ; and again
	mov cx,64000
	rep movsb ;blit
	;restore context
	pop ds
	pop di
	pop es
	pop dx
	pop cx
	pop ax
endm

MBlankScreenMode13 macro
; fils de video ram with zeros
; can be used for initialisation but not for clearing the screen
; better use de videobuffer and MUpdateScreenMode13
    local blankscreenmode13_wait1,blankscreenmode13_wait2
    ;store context
    push ax
    push cx
    push dx
    push es
    push di
    ;set target registers for stosb
    mov ax,0A000h
    mov es,ax
    xor di,di
    ;wait for vblank
    mov dx, 03dah ; VGA status port                 
blankscreenmode13_wait1: ;if in vblank, wait until vblank finishes
    in al, dx
    and al, 8
    jnz blankscreenmode13_wait1                 ; busy wait
blankscreenmode13_wait2:                        ; wait until begin of vblank
    in al, dx
    and al, 8
    jz blankscreenmode13_wait2             ; and again
    ;set fill color
    mov al,0
    mov cx,64000 ;size of video ram
    rep stosb ;blit
    pop di
    pop es
    pop dx
    pop cx
    pop ax
endm

MSetVideoMode macro NewMode
; sets pecified video mode and saves current mode
; needs global variable OldMode that is contained in InstalVar
	push ax
    ;get current mode, set in al
    mov ah,0Fh
    int 10h
    mov OldMode,al
    ;set new mode
    mov ah,00h
    mov al,NewMode
    int 10h
	pop ax
endm

MRestoreVideoMode macro  
; restores old video mode
; needs global variable OldMode that is contained in InstalVar
	push ax
    mov ah,00h
    mov al,OldMode
    int 10h
	pop ax
endm

MDelay macro clockticks
; waits a specified number of colockticks 
; 2 clock ticks ~ 0,10 seconds
; uses int 1AH/00h
local label1
	push dx
	push ax
	push bx
	push si
	xor ah,ah
	xor bx,bx
	int 1Ah	
	mov si,dx
	mov bx,si
label1:
	int 1Ah	
	sub bx,dx
	neg bx
	cmp bx,clockticks	
	mov bx,si
jl label1
	pop si
	pop bx
	pop ax
	pop dx
endm
	
MCopyData macro segsource,offsetsource,segdestination,offsetdestination,nr
; macro created for easy use of PCopyData
	push ax
	mov ax,segsource
	push ax
	mov ax,offsetsource
	push ax
	mov ax,segdestination
	push ax
	mov ax,offsetdestination
	push ax
	mov ax,nr
	push ax
	call PCopyData
	pop ax
endm

IntstalCopyData macro
PCopyData proc
; copies a number of bytes from source to destination
; order to push: segsource,offsetsource,segdestination,offsetdestination,number of bytes 
	;save dynamic link
    push bp
    ;update bp
    mov bp,sp
    ;save context
    push si
    push di
    push ds
    push es
	push dx
    ;load param in bx
	mov dx,[bp+12]		; mov ds,[bp+10] is not allowed!		
	mov ds,dx			; ds points to the segment of the source
	mov dx,[bp+8]		; mov ds,[bp+8] is not allowed!		
	mov es,dx			; es points to the segment of the destination
	mov si,[bp+10]		; si points to the offset of the source
	mov di,[bp+6]		; di points to the offset of the destination
	cld					; the direction flag is cleared, data will be processed from head to tail
	mov cx,[bp+4]		; number of bytes to copy
	; these statements will copy a value from si to di cx times
	rep movsb			
	;restore context
    pop dx
    pop es
    pop ds
    pop di
	pop si
    ;restore bp
    pop bp
    ;return 
    ret 8
PCopyData endp 
endm

MPrintString macro offsetstring
; prints a string to the output
; uses interrupt 21h/09h
	push ax
	push dx
	mov ah,09h
		mov dx,offsetstring
	int 21h
	pop dx
	pop ax
endm

MRandomize macro
; sets the seed of de procedure PRandInt
; seed is set by the current clockticks
	push ax
	push dx
	xor ah, ah		; randomize
		int 1Ah
		mov rand, dx
	pop dx
	pop ax
endm

InstalRandInt macro
PRandInt proc near
; random generater based on mod operation
; ax needs to contain the maximum range and will contain de random result
; to use this procedure you first need to use MRandomize
	; save dynamic link
    push bp			
    mov bp,sp		; update bp
	; save context
	push bx	
	push cx
	push dx
	;code
	mov bx, ax
	mov ax, rand
	mov dx, 16333
	mul dx
	add ax, 25887
	and ax, 7FFFh
	mov rand, ax
	mul bx			; herschalen naar [0, bereik[
	mov cx, 32768
	div cx
	; restore context
	pop dx			
	pop cx
	pop bx
    pop bp			; restore bp
    ret 
PRandInt endp
endm

MPrintInt macro integer
; macro created for easy use of PPrintInt
	push ax
	mov ax,integer
	push ax
	call PPrintInt
	pop ax
endm

InstalPrintInt macro
; print an integer to stdout
; value on tos (16-bit 2's compl)
; needs global variables powersoften and maxint which are contained in InstalVar
PPrintInt proc near ;near procedure
    ;save dynamic link
    push bp
    ;update bp
    mov bp,sp
    ;save context
    push ax
    push bx
    push dx
    push si
    ;load param in bx
    mov bx,[bp+4] ;near proc, one word return address
    ;handle special case of zero
    test bx,0FFFFh
    jnz printint_nonzero
    mov ah,02h
    mov dl,'0'
    int 21h
    jmp printint_done
printint_nonzero:
    ;handle special case of -32768
    cmp bx,-32768
    jnz printint_notintmin
    mov ah,09h
    mov dx,offset maxint
    int 21h
    jmp printint_done
printint_notintmin:
    ;print sign
    test bx,8000h
    jz printint_positive
    ;if negative,print sign and invert
    ;print sign (int 21h, function 02h)
    mov ah,02
    mov dl,'-'
    int 21h
    ;invert sign of bx
    neg bx
printint_positive:
    ;from now on bx is positive
    ;determine largest power of ten smaller than bx
    ;init si to point to first element of powersoften array
    mov si,offset powersoften
    ; while bx<[si] increment si C: while(bx<powersoften[si]) si++
printint_nextpoweroften:
    cmp bx,[si]
    jge printint_powerfound
    add si,2    
    jmp printint_nextpoweroften
printint_powerfound:
    ;ok now print digits
    mov ax,bx
    cwd ;sign extend to DX:AX (32-bit)
    idiv word ptr [si]  ;divide DX:AX by current power of ten, result in AX, remainder in DX
    mov bx,dx  ;move remainder to bx
    mov ah,02h ;print al
    mov dl,al
    ;add 48 to convert to ascii
    add dl,48
    int 21h
    ;was this the last digit? i.e. [si]==1
    cmp word ptr [si],1
    je printint_done
    add si,2   ;increment si
    jmp printint_powerfound ;repeat for next digit
printint_done:
    ;restore context
    pop si
    pop dx
    pop bx
    pop ax
    ;restore bp
    pop bp
    ;return freeing param from stack (2 bytes)
    ret 2
PPrintInt endp

endm 